#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <stdexcept>

#include <getopt.h>

using namespace std;

namespace {
	bool opt_text        = false;
	bool opt_sender      = false;
	bool opt_smsc        = false;
	bool opt_type        = false;
	bool opt_number_type = false;
	bool opt_status      = false;
	bool opt_date        = false;
}

void help(const char * app) {
	cout << "Help:" << endl;
	cout << '\t' << "Usage:" << endl;
	cout << '\t' << '\t' << app << " [options]" << endl;
	cout << '\t' << "Options:" << endl;
	cout << '\t' << '\t' << "-S, --status      Show the status (new/read/outbox/sent) of the message(s)." << endl;
	cout << '\t' << '\t' << "-c, --smsc        Show the smsc by which the message(s) are sent." << endl;
	cout << '\t' << '\t' << "-s, --sender      Show the sender of the message(s)." << endl;
	cout << '\t' << '\t' << "-T, --type        Show the type (sms/...) of the message(s)." << endl;
	cout << '\t' << '\t' << "-t, --text        Show the text contained by the message(s)." << endl;
	cout << '\t' << '\t' << "-n, --number-type Show the type of any shown number (sender/smsc). (international/national/named/...)" << endl;
	cout << '\t' << '\t' << "-d, --date        Show the date/time of the message(s)." << endl;
	cout << '\t' << '\t' << "-a, --all         All the above." << endl;
	cout << '\t' << "Examples:" << endl;
	cout << '\t' << '\t' << app << " < stored_sms" << endl;
	cout << '\t' << '\t' << "smsget -wd /dev/rfcomm0 | " << app << " --text" << endl;
	cout << endl;
	cout << "Please report any bugs or suggestions to: Maurice Bos <m-ou.se@m-ou.se>" << endl;
	cout << endl;
}

inline char hexchar(unsigned int x){
	return x < 10 ? x + '0' : x - 10 + 'A';
}

bool readHexDigit(istream & in, unsigned char & digit) {
	char c;
	in >> c;
	if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')){ digit = c >= 'A' ? c-'A'+10 : c-'0'; return true; }
	return false;
}

bool readHexByte(istream & in, unsigned char & byte) {
	unsigned char h, l;
	if (!readHexDigit(in,h) || !readHexDigit(in,l)) return false;
	byte = (h << 4) + l;
	return true;
}

void show7bitString(unsigned char const * data, size_t length) {
	static const char * const charset[128] = {
		"@", "£", "$", "¥", "è", "é", "ù", "ì", "ò", "Ç", "\\n", "Ø", "ø", "\\r", "Å", "å", "Δ", "_", "Φ", "Γ", "Λ", "Ω", "Π", "Ψ", "Σ", "Θ", "Ξ", 0,
		"Æ", "æ", "ß", "É", " ", "!", "\"", "#", "¤", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
		"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "¡",
		"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "Ä", "Ö", "Ñ", "Ü", "§", "¿",
		"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "ä", "ö", "ñ", "ü", "à"
	};
	size_t offset = 0;
	bool extended = false;
	while(length--){
		unsigned char c = ((data[0] << offset) & 0x7F) | (offset ? data[-1] >> (8 - offset) : 0);
		if (++offset == 8){
			offset = 0 ;
		} else {
			data++;
		}
		if (extended){
			switch(c){
				case 20 : cout.put('^' );  break;
				case 40 : cout.put('{' );  break;
				case 41 : cout.put('}' );  break;
				case 60 : cout.put('[' );  break;
				case 61 : cout.put('~' );  break;
				case 62 : cout.put(']' );  break;
				case 64 : cout.put('|' );  break;
				case 10 : cout << "\\x0C"; break;
				case 47 : cout << "\\\\";  break;
				case 101: cout << "€";     break;
				default: throw "Data contains invalid character.";
			}
			extended = false;
		} else if (const char * s = charset[c]){
			cout << s;
		} else {
			extended = true;
		}
	}
}

void showNumber(vector<unsigned char> const & sms, size_t i, size_t length) {
	while(length > 0){
		cout << hexchar(sms.at(i) & 0xF);
		length--;
		if (length > 0){
			cout << hexchar(sms.at(i) >> 4);
			length--;
		}
		i++;
	}
}

void showUnknown(vector<unsigned char> const & sms, size_t i, size_t length) {
	while(length > 0){
		cout << hexchar(sms.at(i) >> 4);
		length--;
		if (length > 0){
			cout << hexchar(sms.at(i) & 0xF);
			length--;
		}
		i++;
	}
}

void showName(vector<unsigned char> const & sms, size_t i, size_t length) {
	sms.at(i + (length * 7 + 7) / 8 - 1); //Throw out_of_range when needed.
	show7bitString(&sms[i],length);
}

void showSms(unsigned int status, vector<unsigned char> const & sms) {
	try{
		if (opt_status){
			switch(status){
				case 0 : cout << "new"    ; break;
				case 1 : cout << "read"   ; break;
				case 2 : cout << "outbox" ; break;
				case 3 : cout << "sent"   ; break;
				default: cout << "unknown"; break;
			}
			if (opt_smsc || opt_type || opt_sender || opt_date || opt_text) cout << '\t';
		}
		size_t i = 0;
		size_t smsc_size = sms.at(i++);
		if (opt_smsc){
			unsigned char smsc_type = sms.at(i);
			switch(smsc_type){
				case 0x81:
					if (opt_number_type) cout << "number\t";
					showNumber(sms,i+1,(smsc_size-1)*2 - (sms.at(i+smsc_size-1) >> 4 == 0xF ? 1 : 0));
					break;
				case 0x91:
					if (opt_number_type) cout << "international\t";
					cout << '+';
					showNumber(sms,i+1,(smsc_size-1)*2 - (sms.at(i+smsc_size-1) >> 4 == 0xF ? 1 : 0));
					break;
				default:
					if (opt_number_type) cout << "unknown" << int(smsc_type) << '\t';
					showUnknown(sms,i+1,(smsc_size-1)*2);
					break;
			}
			if (opt_type || opt_sender || opt_date || opt_text) cout << '\t';
		}
		
		i += smsc_size;
		
		unsigned char type = sms.at(i);
		
		if (opt_type){
			switch(type){
				case 0 : cout << "sms";                  break;
				default: cout << "unknown" << int(type); break;
			}
			if (opt_sender || opt_date || opt_text) cout << '\t';
		}
		
		size_t sender_size = sms.at(i+1);
		
		if (opt_sender){
			unsigned char sender_type = sms.at(i+2);
			switch(sender_type){
				case 0x81:
					if (opt_number_type) cout << "number\t";
					showNumber(sms,i+3,sender_size);
					break;
				case 0x91:
					if (opt_number_type) cout << "international\t";
					cout << '+';
					showNumber(sms,i+3,sender_size);
					break;
				case 0xD0:
					if (opt_number_type) cout << "named\t";
					showName(sms,i+3,sender_size*4/7);
					break;
				default:
					if (opt_number_type) cout << "unknown" << int(sender_type) << '\t';
					showNumber(sms,i+3,sender_size);
					break;
			}
			if (opt_date || opt_text) cout << '\t';
		}
		
		i += 3 + (sender_size + 1) / 2;
		
		unsigned char protocol = sms.at(i++);
		unsigned char data_encoding = sms.at(i++);
		
		if (opt_date){		
			unsigned int year   = (sms.at(i+0) & 0xF) * 10 + (sms.at(i+0) >> 4);
			unsigned int month  = (sms.at(i+1) & 0xF) * 10 + (sms.at(i+1) >> 4);
			unsigned int day    = (sms.at(i+2) & 0xF) * 10 + (sms.at(i+2) >> 4);
			unsigned int hour   = (sms.at(i+3) & 0xF) * 10 + (sms.at(i+3) >> 4);
			unsigned int minute = (sms.at(i+4) & 0xF) * 10 + (sms.at(i+4) >> 4);
			unsigned int second = (sms.at(i+5) & 0xF) * 10 + (sms.at(i+5) >> 4);
			int timezone = (sms.at(i+6) & 0x80 ? -1 : 1) * (sms.at(i+6) & 0xF) * 10 + ((sms.at(i+6) >> 4) & 0x7);
			cout << (year < 90 ? year + 2000 : year + 1900) << '-' << month << '-' << day << ' ' << hour << ':' << minute << ':' << second << " GMT";
			if (timezone > 0){
				cout << '+' << (timezone / 4);
				if (timezone % 4) cout << ':' << (timezone * 15);
			} else if (timezone < 0) {
				cout << '-';
				timezone = -timezone;
				cout << (timezone / 4);
				if (timezone % 4) cout << ':' << (timezone * 15);
			}
			if (opt_text) cout << '\t';
		}
		
		i += 7;
		
		if (opt_text){
			size_t text_length = sms.at(i++);
			if (data_encoding == 0x00){
				sms.at(i + (text_length * 7 + 7) / 8 - 1); //Throw out_of_range when needed.
				show7bitString(&sms[i],text_length);
			} else {
				cerr << "[Unknown data encoding]";
			}
		}
		
		cout << endl;
	} catch(out_of_range const & e) {
		throw "Incomplete sms data.";
	}
}

int main(int argc, char * * argv) {
	
	if (argc == 2 && (argv[1] == string("-?") || argv[1] == string("-h") || argv[1] == string("--help"))){
		help(argv[0]);
		return 0;
	}
	
	struct option const long_options[] = {
		{ "text"       , no_argument, 0, 't' },
		{ "sender"     , no_argument, 0, 's' },
		{ "smsc"       , no_argument, 0, 'c' },
		{ "type"       , no_argument, 0, 'T' },
		{ "status"     , no_argument, 0, 'S' },
		{ "number-type", no_argument, 0, 'n' },
		{ "date"       , no_argument, 0, 'd' },
		{ "all"        , no_argument, 0, 'a' },
		{ 0, 0, 0, 0 }
	};

	while(true){
		int option = getopt_long(argc, argv, "atscnTS", long_options, 0);
		if (option < 0) break;
		switch(option){
			case 't': opt_text        = true; break;
			case 's': opt_sender      = true; break;
			case 'c': opt_smsc        = true; break;
			case 'n': opt_number_type = true; break;
			case 'T': opt_type        = true; break;
			case 'S': opt_status      = true; break;
			case 'd': opt_date        = true; break;
			case 'a':
				opt_text        = true;
				opt_sender      = true;
				opt_smsc        = true;
				opt_number_type = true;
				opt_type        = true;
				opt_status      = true;
				opt_date        = true;
				break;
		}
	}
	
	if (opt_type == false && opt_status == false && opt_text == false && opt_sender == false && opt_smsc == false && opt_date == false){
		opt_text   = true;
		opt_sender = true;
	}
	
	try{
		while(!cin.eof()){
			unsigned int index;
			unsigned int status;
			size_t size;
			cin >> index;
			if (cin.eof()) break;			
			if (!(cin >> status >> size)) throw "Invalid data.";
			vector<unsigned char> sms(size);
			for(size_t i = 0; i < size; i++) if (!readHexByte(cin,sms[i])) throw "Invalid sms data.";
			showSms(status,sms);
		}
	} catch(char const * error) {
		cerr << "Error: " << error << endl;
		return -1;
	}
	return 0;
}

